package com.company.nuwa.redis;

import com.company.nuwa.redis.common.KeyObj;
import com.company.nuwa.redis.common.RedisConfiguration;
import com.company.nuwa.redis.wrapper.*;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>redis ext template</p >
 *
 * @author Nikola Tesla
 * @version 1.0
 * @date 2020/10/29 15:44
 */
@Slf4j
public class RedisExtTemplate extends StringRedisTemplate {

    private final RedisHashOperationsWrapper hashOps = new RedisHashOperationsWrapper(this);

    private final RedisValueOperationsWrapper valueOps = new RedisValueOperationsWrapper(this);

    private final RedisListOperationsWrapper listOps = new RedisListOperationsWrapper(this);

    private final RedisSetOperationsWrapper setOps = new RedisSetOperationsWrapper(this);

    private final RedisZSetOperationsWrapper zSetOps = new RedisZSetOperationsWrapper(this);

    private final RedisConfiguration redisConfiguration;

    public RedisExtTemplate(RedisConnectionFactory connectionFactory, RedisConfiguration redisConfiguration) {
        super();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
        this.redisConfiguration = redisConfiguration;
    }

    /**
     * Hash操作封装
     *
     * @return
     */
    public RedisHashOperationsWrapper opsForHashWrapper() {
        return hashOps;
    }

    /**
     * String操作封装
     *
     * @return
     */
    public RedisValueOperationsWrapper opsForValueWrapper() {
        return valueOps;
    }

    /**
     * List操作封装
     *
     * @return
     */
    public RedisListOperationsWrapper opsForListWrapper() {
        return listOps;
    }

    /**
     * Set操作封装
     *
     * @return
     */
    public RedisSetOperationsWrapper opsForSetWrapper() {
        return setOps;
    }

    /**
     * ZSet操作封装
     *
     * @return
     */
    public RedisZSetOperationsWrapper opsForZSetWrapper() {
        return zSetOps;
    }

    /**
     * 批量获取redis-key，转换list实体对象，若某些key在redis中未获取到对应的值，则会尝试从valueMapper中获取
     * <p>
     * 解析对象所使用的序列化方式 {@link RedisConfiguration#getObjectCacheRedisSerializer()}，
     *
     * @param keyObjs         keyObjs对象
     * @param classType       返回对应的实体类型
     * @param beanKeyMapper   该实体的key值，即{@link KeyObj}中的{@code param}
     * @param valueDataSource 若某个缓存key中没有对应的缓存数据，则从{@code valueDataSource} 中获取数据
     * @return 实体列表
     */
    public <T> List<T> finds(List<KeyObj> keyObjs, Class<T> classType, Function<T, String> beanKeyMapper, Function<String, T> valueDataSource) {
        List<String> allKeys = keyObjs.stream()
                .map(keyObj -> keyObj.getGenKey().apply(keyObj.getParam()))
                .map(key -> redisConfiguration.getKeyPrefix() + key)
                .distinct()
                .collect(Collectors.toList());

        List<String> params = keyObjs.stream()
                .map(KeyObj::getParam)
                .distinct()
                .collect(Collectors.toList());

        // 最大100个key一次查询
        List<List<String>> multiKeys = Lists.partition(allKeys, 100);

        List<T> values = multiKeys.stream()
                .map(keys -> opsForValueWrapper().multiGet(keys, classType, redisConfiguration.getObjectCacheRedisSerializer()))
                .flatMap(List::stream)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        Map<String, T> valueMap = values.stream().collect(Collectors.toMap(beanKeyMapper, Function.identity(), (v1, v2) -> v1));

        return params.stream()
                .map(param -> valueMap.getOrDefault(param, valueDataSource.apply(param)))
                .collect(Collectors.toList());
    }

}
